#ifndef _COMMAND_H_	
#define _COMMAND_H_
#pragma once
#include <queue>

#include "buffer.h"
#include "Int128.h"
#include "util.h"

// command message define.
#define CMD_MSG_NULL   			  0x00000000
#define CMD_MSG_WAIT_TIMEOUT      0x00000005
#define CMD_MSG_EXIT              0xffffffff
#define CMD_MSG_USER              0x10000000

class tCommand;

class tParamObject
{
	bool	m_FreeFlag;
public:
	tParamObject():m_FreeFlag(true){}
	void DontFree(){ m_FreeFlag = false;}
	void Freed(){ m_FreeFlag = false;}
	bool IsNeedFree(){ return m_FreeFlag;}
	/* If assert occur, check whether you release the data in
	 * your derived class, and called the Freed().
	 */
	virtual ~tParamObject(){assert(!m_FreeFlag);}
};

// define the message body.
struct tMsg
{
	union uData
	{
		int				i32;
		unsigned int	u32;
		char			c;
		unsigned int	uc;
		long			l;
		unsigned long	ul;
		INT64			i64;
		UINT64			u64;
		void*			ptr;
	};
	uData m_uData1;
	uData m_uData2;
	
    long MsgID;					// Message ID.
    long Data1;					// 32 bits data.
    long Data2;					// 32 bits data.
    UINT64 Data3;				// 64 bits data.
    UINT64 Data4;				// 64 bits data.
	tInt128 Data128;			// 128 bits data.
    void* DataPtr;				// pointer.
    int DataSize;				// data size of pointer.
    tCommand* SyncCommand;		// used to sync sender.
	bool Ref;					// reference msg donot need to free
	tParamObject * Param;

	tMsg();

	// deprecated member.
    UINT64 MsgGetInt8()
	{
	    return Data3;
	}
	// deprecated member.
    void MsgSetInt8(UINT64 v)
	{
	    Data3 = v;
	}

	//---------------------------------------------------------------
	// Load/Save this message to a memory buffer.
	//---------------------------------------------------------------
	// Parameter:
	//     msgdata : The buffer instance which used to read/write. For
	//               load message, the msgdata must have data and for
	//               Save message, the msgdata must be empty.
	// Return Value:
	//     It always return 0.
	// Exception:
	//     All memory related exception may be raised.
	//---------------------------------------------------------------
    int Load(tStreamBuffer & msgdata);
    int Save(tStreamBuffer & msgdata);
	//---------------------------------------------------------------
	
	//---------------------------------------------------------------
	// Unblock the sender of this message. When sender send a message
	// with flag wait is true, it will block to get a response message
	// from handler of message. When handler find the SyncCommand in
	// message body is not NULL, it will call SyncSender to wake up
	// the sender thread and notify it the message has been processed.
	//---------------------------------------------------------------
    void SyncSender();
	//---------------------------------------------------------------
    
	//===============================================================
	//flag indicate that the DataPtr is alloced only for once use, 
	//and need free after used. 
	void SetFreeFlag(bool free);
    //================================================================
    // Free the allocated data when loading from buffer.
    //----------------------------------------------------------------
    void FreeData();
	void ReleaseParam();
    //================================================================

};

// command queue.
class tCommand
{
protected:
    std::queue<tMsg> MsgQueue;		// queue for storing command.

#ifndef _linux_
    HANDLE hNewMsgEvent;			// event for notify new command.
    HANDLE hQueueLock;				// lock for multi-access MsgQueue.
#else
    tMutex QueueLock;								
    tSemaphore Sema;
#endif
	
	//---------------------------------------------------------------
	// Lock/Unlock the message queue Resource. Internal use only.
	//---------------------------------------------------------------
    void LockQueue();
    void UnLockQueue();
	//---------------------------------------------------------------

public:
    tCommand(void);
    virtual ~tCommand(void);

	//---------------------------------------------------------------
    // Get next command in the msg loop.
	//---------------------------------------------------------------
	// Parameters:
	//    msg       : The tMsg structure to save the message.
	//    bWait     : If the queue is empty, whether to wait until a
	//                message is come out.
	//    wait_time : How long to wait for message. If its zero, and
	//                bWait is true, it will wait infinitely.
	// Retrun Value:
	//    1  : Get a message or timeout.
	//    -1 : if bWait is false, and No message can be gotten.
	//    0  : Receive a CMD_MSG_EXIT.
	//---------------------------------------------------------------
    int GetCommand(tMsg* msg, bool bWait, unsigned int wait_time = 0);
	//---------------------------------------------------------------

	//---------------------------------------------------------------
    // Send the msg to msg loop.
	//---------------------------------------------------------------
	// Parameters:
	//     msg   : The tMsg data to be sent.
	//     bWait : Whether to wait for this message to be handled.
	// Return Value:
	//     Always return 0.
	//---------------------------------------------------------------
    int SendCommand(tMsg msg, bool bWait);
	//---------------------------------------------------------------

	//---------------------------------------------------------------
	// Clean all the message in the queue.
	//---------------------------------------------------------------
	void FlushCommandQueue();

};

#define MSGPARAM(msg, type, name) \
	type *name = new type();\
	msg.Param = name;


#define GETMSGPARAM(msg, type, name) \
	type *name = (type*)msg.Param;\
	name->DontFree();

#endif
